/**
 * Licensed to Open-Ones Group under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Open-Ones Group licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a
 * copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.hoangqp.android.stardictcore;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.util.Log;

public class StarDict {

	/** number of the nearest word that is displayed. */
	private final int nearest = 10;

	/** Dict directory.(path to the .dict file). */
	private String strURL = null;

	/** decide if object has loaded the entries. */
	private boolean boolAvailable = false;

	/** ifo file. */
	private IfoFile ifoFile = null;

	/** idx file. */
	private IdxFile idxFile = null;

	/** dict file. */
	private DictFile dictFile = null;
	
	/** zip dict file */
	private StarDictDictFileParser starDictFile = null;

	/**
	 * Constructor to load dictionary with given path.
	 * 
	 * @param url
	 *            Path of one of stardict file or Path of folder contains
	 *            stardict files
	 * @throws FileNotFoundException 
	 * @deprecated using StarDict.loadDict(String url)
	 */
	public StarDict(String url) throws FileNotFoundException {
		File file = new File(url);

		if (!file.isDirectory()) {
			strURL = getFileNameWithoutExtension(url);
			Log.d("strURL", strURL);
			ifoFile = new IfoFile(strURL + ".ifo");
			idxFile = new IdxFile(strURL + ".idx", ifoFile.getLongWordCount(),
					ifoFile.getLongIdxFileSize());
			dictFile = new DictFile(strURL + ".dict");
		} else {
			String[] list = file.list();
			// Map<file extension,file name>
			Map<String, String> fileMap = new HashMap<String, String>();

			// Build table to mapping the file extension and file name
			for (int i = list.length - 1; i >= 0; i--) {
				if (list[i].endsWith(".ifo")) {
					fileMap.put(".ifo", list[i]);
				} else if (list[i].endsWith(".idx")) {
					fileMap.put(".idx", list[i]);
				} else if (list[i].endsWith(".dict")) {
					fileMap.put(".dict", list[i]);
				}
			}

			ifoFile = new IfoFile(url + File.separator + fileMap.get(".ifo"));
			idxFile = new IdxFile(url + File.separator + fileMap.get(".idx"),
					ifoFile.getLongWordCount(), ifoFile.getLongIdxFileSize());
			dictFile = new DictFile(url + File.separator + fileMap.get(".dict"));
		}

		if (ifoFile.isBoolIsLoaded() && idxFile.isLoaded()) {
			boolAvailable = true;
		}
	}

	/**
	 * Create a new object of StarDict with files: ifo, idx, dict.
	 * 
	 * @param ifoFile
	 *            info file
	 * @param idxFile
	 *            index file
	 * @param dictFile
	 *            data dictionary file
	 */
	public StarDict(IfoFile ifoFile, IdxFile idxFile, DictFile dictFile) {
		this.ifoFile = ifoFile;
		this.idxFile = idxFile;
		this.dictFile = dictFile;

		if (ifoFile.isBoolIsLoaded() && idxFile.isLoaded()) {
			boolAvailable = true;
		}
	}

	/**
	 * Load dictionary from the folder.
	 * 
	 * @param url
	 *            path of folder
	 * @return object of StarDict
	 * @throws FileNotFoundException 
	 */
	public static StarDict loadDict(String url) throws FileNotFoundException {
		File file = new File(url);
		String ifoFilePath;

		IfoFile ifoFile = null;
		IdxFile idxFile = null;
		DictFile dictFile = null;

		if (!file.isDirectory()) {
			String filePathNoExt = getFileNameWithoutExtension(url);
			Log.d("filePathNoExt=", filePathNoExt);

			ifoFilePath = filePathNoExt + ".ifo";
			if (new File(ifoFilePath).isFile()) {
				ifoFile = new IfoFile(ifoFilePath);
			} else {
				return null;
			}

			idxFile = new IdxFile(filePathNoExt + ".idx",
					ifoFile.getLongWordCount(), ifoFile.getLongIdxFileSize());
			dictFile = new DictFile(filePathNoExt + ".dict");
		} else {
			String[] list = file.list();
			// Map<file extension,file name>
			Map<String, String> fileMap = new HashMap<String, String>();

			// Build table to mapping the file extension and file name
			for (int i = list.length - 1; i >= 0; i--) {
				if (list[i].endsWith(".ifo")) {
					fileMap.put(".ifo", list[i]);
				} else if (list[i].endsWith(".idx")) {
					fileMap.put(".idx", list[i]);
				} else if (list[i].endsWith(".dict")) {
					fileMap.put(".dict", list[i]);
				}
			}

			ifoFile = new IfoFile(url + File.separator + fileMap.get(".ifo"));
			idxFile = new IdxFile(url + File.separator + fileMap.get(".idx"),
					ifoFile.getLongWordCount(), ifoFile.getLongIdxFileSize());
			dictFile = new DictFile(url + File.separator + fileMap.get(".dict"));

		}

		if ((ifoFile == null) || (idxFile == null) || (dictFile == null)) {
			return null;
		}

		StarDict dict = new StarDict(ifoFile, idxFile, dictFile);
		return dict;
	}

	/**
	 * get book name of dictionary.
	 * 
	 * @return Book name
	 */
	public String getDictName() {
		return ifoFile.getStrBookname().replace("\r", "").trim();
	}

	/**
	 * get book version.
	 * 
	 * @return version of a dictionary
	 */
	public String getDictVersion() {
		return ifoFile.getStrVersion();
	}

	/**
	 * get amount of words in a StarDict dictionary (within 3 files).
	 * 
	 * @return a long totalWord.
	 */
	public int getTotalWords() {
		return getWordEntry().size();
	}

	/**
	 * get word content from an idx. let say the stardict-dictd-easton-2.4.2, we
	 * give this method the idx 1000 and it return us the "diana".
	 * 
	 * @param idx
	 *            index
	 * @return word
	 */
	public String getWordByIndex(int idx) {
		String word = getWordEntry().get(idx).getStrLwrWord();
		return word;
	}

	public String lookupWord(long offset, long size){
		return dictFile.getWordData(offset,
				size);
	}
	/**
	 * lookup a word by its index.
	 * 
	 * @param idx
	 *            index of a word
	 * @return word data
	 */
	public String lookupWord(int idx) {
		if (idx < 0 || idx >= idxFile.getLongWordCount()) {
			return "not found";
		}
		WordEntry tempEntry = idxFile.getEntryList().get((int) idx);
		return dictFile.getWordData(tempEntry.getLongOffset(),
				tempEntry.getLongSize());
	}
	
	 /**
     * lookup a word.
     * @param word that is looked up in database.
     * @return word data
     */
	public String lookupWord(String word) {
        if (!boolAvailable) {
            return "the dictionary is not available";
        }
        int idx = (int) idxFile.findIndexForWord(word);

        return lookupWord(idx);
    }
	
	/**
     * get a list of word entry.
     * @return list of word entry
     */
    public List<WordEntry> getWordEntry() {
        return idxFile.getEntryList();
    }
    
    /**
     * load index file and info file.
     */
    public void reLoad() {
        boolAvailable = false;
        ifoFile.reload();
        idxFile.reload();

        if (ifoFile.isBoolIsLoaded() && idxFile.isLoaded()) {
            boolAvailable = true;
        }
    }
    
    /**
     * get the nearest of the chosen word.
     * @param word that is looked up in database
     * @return a list of nearest word.
     */
    public List<Word> getNearestWords(String word) {
        if (boolAvailable) {
            int idx = (int) idxFile.findIndexForWord(word);
            int nMax = nearest + idx;
            if (nMax > idxFile.getLongWordCount()) {
                nMax = (int) idxFile.getLongWordCount();
            }
            List<Word> wordList = new ArrayList<Word>();
            for (int i = idx; i < nMax; i++) {
                if (i != 0) {
                    Word tempWord = new Word();
                    tempWord.setStrWord(idxFile.getEntryList().get(i).getStrWord());
                    tempWord.setIndex(i);
                    wordList.add(tempWord);
                }
            }
            return wordList;
        }
        return null;
    }
    
    /**
     * check if a word is in dictionary.
     * @param word that is looked up in database
     * @return true if exists, false otherwise
     */
    public boolean existWord(String word) {
        int wordIndex = (int) idxFile.findIndexForWord(word);

        if (wordIndex >= idxFile.getLongWordCount()) {
            return false;
        }

        String lwrWord = word.toLowerCase();
        if (lwrWord.equals(idxFile.getEntryList().get(wordIndex).getStrLwrWord())) {
            return true;
        }

        return false;
    }
    
    /**
     * Add list of word to idx, dict file, modify size .ifo file.
     * @param pWord word that is added
     * @param pMean word mean
     * @return true if success
     */
    public boolean addListOfWords(String[] pWord, String[] pMean) {
        if (pWord.length != pMean.length || pWord.length == 0) {
            return false;
        }
        try {
            for (int i = 0; i < pWord.length; i++) {
                String strLwrWord = pWord[i].toLowerCase();
                int pos = (int) idxFile.findIndexForWord(strLwrWord);
                boolean bExist = false;
                if (pos < (int) idxFile.getLongWordCount()) {
                    if (strLwrWord.compareTo(((WordEntry) idxFile.getEntryList().get(pos)).getStrLwrWord()) == 0) {
                        bExist = true;
                    }
                }
                long nextOffset = dictFile.addData(pMean[i]);
                if (nextOffset >= 0) {
                    if (!bExist) {
                        idxFile.addEntry(pWord[i], nextOffset, pMean[i].length(), pos);
                    } else {
                        WordEntry tempEntry = idxFile.getEntryList().get(pos);
                        tempEntry.setLongOffset(nextOffset);
                        tempEntry.setLongSize(pMean[i].length());
                    }
                }
            }
            idxFile.write();
            ifoFile.setLongIdxFileSize(idxFile.getLongIdxFileSize());
            ifoFile.setLongWordCount(idxFile.getLongWordCount());
            ifoFile.write();
        } catch (Exception ex) {
            return false;
        }
        return true;
    }
    
    /**
     * Add a word to .dict file and .idx file, modify the size of ifo file.
     * @param word word that is needed to add.
     * @param mean word meaning.
     * @return true if add complete.
     */
    public boolean addOneWord(String word, String mean) {
        String[] pWord = new String[1];
        String[] pMean = new String[1];
        pWord[0] = word;
        pMean[0] = mean;

        return addListOfWords(pWord, pMean);
    }
    
    /**
     * Get file name without extension. For example: input: a:\b.a - output: a:\b
     * @param url path of a file
     * @return original file name
     */
    public static String getFileNameWithoutExtension(String url) {
        int dot = url.lastIndexOf(".");

        return (dot > -1) ? url.substring(0, dot) : null;
    }

    /**
     * get extension of file.
     * @param url path to file
     * @return extension of file
     */
    public static String getExtension(String url) {
        int dot = url.lastIndexOf(".");
        return url.substring(dot + 1);
    }
    
}
